
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class VShadowCasterPass : VScriptableRenderPass
{
    private ShadowSetting m_ShadowSetting;
    private int m_shadowMapResolution;
    private Matrix4x4[] _worldToCascadeShadowMapMatrices = new Matrix4x4[4];
    private Vector4[] _cascadeCullingSpheres = new Vector4[4];
    private Matrix4x4 _WorldToShadowMat;
    private static class MainLightShadowConstantBuffer
    {
        public static int _WorldToMainLightCascadeShadowMapSpaceMatrices;
        public static int _CascadeCullingSpheres;
        public static int _ShadowParams;
    }
    static Matrix4x4 GetWorldToShadowMapSpaceMatrix(Matrix4x4 proj, Matrix4x4 view,Vector4 cascadeOffsetAndScale)
    {
        //检查平台是否zBuffer反转,一般情况下，z轴方向是朝屏幕内，即近小远大。但是在zBuffer反转的情况下，z轴是朝屏幕外，即近大远小。
        if (SystemInfo.usesReversedZBuffer)
        {
            proj.m20 = -proj.m20;
            proj.m21 = -proj.m21;
            proj.m22 = -proj.m22;
            proj.m23 = -proj.m23;
        }
            
        Matrix4x4 worldToShadow = proj * view;
            
        // xyz = xyz * 0.5 + 0.5. 
        // 即将xy从(-1,1)映射到(0,1),z从(-1,1)或(1,-1)映射到(0,1)或(1,0)
        var textureScaleAndBias = Matrix4x4.identity;
        //x = x * 0.5 + 0.5
        textureScaleAndBias.m00 = 0.5f;
        textureScaleAndBias.m03 = 0.5f;

        //y = y * 0.5 + 0.5
        textureScaleAndBias.m11 = 0.5f;
        textureScaleAndBias.m13 = 0.5f;

        //z = z * 0.5 = 0.5
        textureScaleAndBias.m22 = 0.5f;
        textureScaleAndBias.m23 = 0.5f;

        //再将uv映射到cascadeShadowMap的空间
        var cascadeOffsetAndScaleMatrix = Matrix4x4.identity;

        //x = x * cascadeOffsetAndScale.z + cascadeOffsetAndScale.x
        cascadeOffsetAndScaleMatrix.m00 = cascadeOffsetAndScale.z;
        cascadeOffsetAndScaleMatrix.m03 = cascadeOffsetAndScale.x;

        //y = y * cascadeOffsetAndScale.w + cascadeOffsetAndScale.y
        cascadeOffsetAndScaleMatrix.m11 = cascadeOffsetAndScale.w;
        cascadeOffsetAndScaleMatrix.m13 = cascadeOffsetAndScale.y;

        return cascadeOffsetAndScaleMatrix * textureScaleAndBias * worldToShadow;
    }
    private void SetupShadowCascade(
        ScriptableRenderContext context,
        CommandBuffer cmd,
        Vector2 offsetInAtlas,
        int resolution,
        ref Matrix4x4 matrixView,ref Matrix4x4 matrixProj)
    {
        cmd.Clear();
        cmd.SetViewport(new Rect(offsetInAtlas.x,offsetInAtlas.y,resolution,resolution));
        //设置view&proj矩阵
        cmd.SetViewProjectionMatrices(matrixView,matrixProj);
       
        context.ExecuteCommandBuffer(cmd);
    }
    private static int GetShadowMapResolution(Light light)
    {
        switch(light.shadowResolution){
            case LightShadowResolution.VeryHigh:
                return 2048;
            case LightShadowResolution.High:
                return 1024;
            case LightShadowResolution.Medium:
                return 512;
            case LightShadowResolution.Low:
                return 256;
        }
        return 256;
    }
    private static readonly ShaderTagId k_ShaderTagId = new ShaderTagId("ShadowCaster");
    
    RenderTargetHandle m_MainLightShadowmap;
    internal RenderTexture m_MainLightShadowmapTexture=null;
    
  
    internal RenderTextureDescriptor descriptor { get; set; }
    internal bool allocateDepth { get; set; } = true;
    internal ShaderTagId shaderTagId { get; set; } = k_ShaderTagId;
    public VShadowCasterPass(RenderPassEvent renderPassEvent):base(renderPassEvent)
    {
        MainLightShadowConstantBuffer._ShadowParams = Shader.PropertyToID("_ShadowParams");
        MainLightShadowConstantBuffer._WorldToMainLightCascadeShadowMapSpaceMatrices =
            Shader.PropertyToID("_VWorldToMainLightCascadeShadowMapSpaceMatrices");
        MainLightShadowConstantBuffer._CascadeCullingSpheres =
            Shader.PropertyToID("_VCascadeCullingSpheres");
        
        m_MainLightShadowmap.Init("_VMainShadowMap");
        
        
    }
    public bool Setup(ref VRenderingData renderingData)
    {
        m_ShadowSetting = renderingData.shadowSetting;
        var lightData = renderingData.lightData;
        var mainLight = lightData.visibleLights[lightData.mainLightIndex];
        var lightComp = mainLight.light;
        m_shadowMapResolution = GetShadowMapResolution(lightComp);
        var renderTargetWidth = m_shadowMapResolution;
        var renderTargetHeight = m_shadowMapResolution;
       
            m_MainLightShadowmapTexture = ShadowUtils.GetTemporaryShadowTexture(
                renderTargetWidth, renderTargetHeight, 32);
        
            
         
        return true;
    }

    private void ExeForCSM(ScriptableRenderContext context, CommandBuffer cmd,
        ref CullingResults cullingResults, ref VLightData lightData)
    {
        var cascadeRatio = m_ShadowSetting.cascadeRatio;
        var cascadeAtlasGridSize = Mathf.CeilToInt(Mathf.Sqrt(m_ShadowSetting.cascadeCount));
        var cascadeResolution = m_shadowMapResolution / cascadeAtlasGridSize;
       

        var mainLight = lightData.visibleLights[lightData.mainLightIndex];
        var lightComp = mainLight.light;

        for (int i = 0; i < m_ShadowSetting.cascadeCount; i++)
        {

            var x = i % cascadeAtlasGridSize;
            var y = i / cascadeAtlasGridSize;

            //计算当前级别的级联阴影在Atlas上的偏移位置
            var offsetInAtlas = new Vector2(x * cascadeResolution, y * cascadeResolution);

            //get light matrixView,matrixProj,shadowSplitData
            cullingResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(
                lightData.mainLightIndex, i, m_ShadowSetting.cascadeCount,
                cascadeRatio, cascadeResolution, lightComp.shadowNearPlane,
                out var matrixView, out var matrixProj, out var shadowSplitData);

            //generate ShadowDrawingSettings
            ShadowDrawingSettings shadowDrawSetting = new ShadowDrawingSettings(
                cullingResults, lightData.mainLightIndex);
            shadowDrawSetting.splitData = shadowSplitData;

            //设置Cascade相关参数
            SetupShadowCascade(context, cmd, offsetInAtlas, cascadeResolution, ref matrixView, ref matrixProj);

            //绘制阴影
            context.DrawShadows(ref shadowDrawSetting);

            //计算Cascade ShadowMap空间投影矩阵和包围圆
            var cascadeOffsetAndScale = new Vector4(offsetInAtlas.x,offsetInAtlas.y,cascadeResolution,cascadeResolution) / m_shadowMapResolution;
            var matrixWorldToShadowMapSpace = 
                GetWorldToShadowMapSpaceMatrix(matrixProj,matrixView,cascadeOffsetAndScale);
            _worldToCascadeShadowMapMatrices[i] = matrixWorldToShadowMapSpace;
            _cascadeCullingSpheres[i] = shadowSplitData.cullingSphere;
            
          
            
        }
        //setup shader params
        Shader.SetGlobalMatrixArray(MainLightShadowConstantBuffer._WorldToMainLightCascadeShadowMapSpaceMatrices,_worldToCascadeShadowMapMatrices);
        Shader.SetGlobalVectorArray(MainLightShadowConstantBuffer._CascadeCullingSpheres,_cascadeCullingSpheres);
        //将阴影的一些数据传入Shader
        Shader.SetGlobalVector(MainLightShadowConstantBuffer._ShadowParams,new Vector4(lightComp.shadowBias,lightComp.shadowNormalBias,lightComp.shadowStrength,m_ShadowSetting.cascadeCount));

        {
            
        }
    }

    public override void OnCameraCleanup(CommandBuffer cmd)
    {
        
        if (m_MainLightShadowmapTexture)
        {
            RenderTexture.ReleaseTemporary(m_MainLightShadowmapTexture);
            m_MainLightShadowmapTexture = null;
        }
    }
    public override void Execute(ScriptableRenderContext context, ref VRenderingData renderingData)
    {
        var cullingResults = renderingData.cullResults;
        
        
        var lightData = renderingData.lightData;
        if (!cullingResults.GetShadowCasterBounds(lightData.mainLightIndex, out var lightBounds))
        {
            Shader.SetGlobalVector(MainLightShadowConstantBuffer._ShadowParams,new Vector4(0,0,0,0));
            return;
        }
        
        
        CommandBuffer cmd = CommandBufferPool.Get();
        cmd.name = "Shadow Caster";
        //cmd.BeginSample("Shadow Caster");
        
        cmd.SetGlobalTexture(m_MainLightShadowmap.id, m_MainLightShadowmapTexture);
        var renderTargetIdentifier = new RenderTargetIdentifier(m_MainLightShadowmapTexture);
        cmd.SetRenderTarget(renderTargetIdentifier,renderTargetIdentifier);
        cmd.ClearRenderTarget(true, false, Color.clear);       
        context.ExecuteCommandBuffer(cmd);
        
        ExeForCSM(context,cmd,ref cullingResults,ref lightData);
        
        
        
        //cmd.EndSample("Shadow Caster");
        cmd.Clear();
        
        CommandBufferPool.Release(cmd);
        
        
    }
}